/*
 * ********************************************************************************
 * Project:        GAMES, 7th Framework Programme, co-funded by European Commission
 * Product:        ESMI Prototype
 * File:           CorrelationCalculator.java (UTF-8)
 * Reviewd by:     Alessandro Rossi <alessandro.rossi@eng.it>
 * Reviewd on:     11-giu-2012 15.32.21
 * 
 * (C) Copyright 2010-2012 Engineering Ingegneria Informatica S.p.A.
 * 
 * This file is part of the Energy Sensing & Monitoring Infrastructure of GAMES.
 * ********************************************************************************
 * 
 * The terms of the license are under discussion and will be defined.
 * Until then, the distribution of this file is restricted to the members of the GAMES Consortium.
 */
package eu.games.esmi.ieat.indicatorrelations.data;

import java.util.Hashtable;
import java.util.Enumeration;
import java.util.StringTokenizer;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.File;
import java.io.InputStream;
import java.io.FileInputStream;

import weka.core.converters.ArffLoader;
import weka.core.Instances;
import weka.clusterers.Clusterer;



/**
 *
 * @author darnone
 */
public class ClusterMiner {
    
    private boolean firstLoad;
    private int numberOfInstances;
    private Hashtable<String,String[]> dataset = new Hashtable<String,String[]>();
    private String msg;
    private ArffLoader loader;
    private Instances m_instances;
    private Clusterer curClusterer = null;
    private int numberOfClusters = 0;
    
    public ClusterMiner(){
        firstLoad = true;
        loader = new ArffLoader();
    }
    
    public Clusterer getClusterer(){
        return curClusterer;
    }

    public boolean load(String gpikpi_id, String[] values){
       
        if ((!firstLoad)){
            if (numberOfInstances!=values.length){
                msg = "number of instances must be always the same";
                return false;
            }
            if (dataset.containsKey(gpikpi_id)){
                msg = "two different GPI/KPI cannot have the same name";
                dataset.put(gpikpi_id, values);            
                return false;
            }
            dataset.put(gpikpi_id, values);
            return true;
        }
        numberOfInstances = values.length;
        dataset.put(gpikpi_id, values);
        firstLoad = false;
        return true;
    };
    
    private void createARFFFile(){
        String type = "green, yellow, red";
        try{
            File instancesFile = new File("tempARFFFile.arff");
            FileWriter fstream = new FileWriter(instancesFile);
            BufferedWriter out = new BufferedWriter(fstream);
            int numberOfAttributes = dataset.size();
            
            Enumeration<String> attributeNames = dataset.keys();            
            String[][] attributeValuesMatrix = new String[numberOfAttributes][numberOfInstances];
            out.write("@relation 'temprelation'\n");
            int counter = 0;
            String currentAttributeName = "";
            while (attributeNames.hasMoreElements()){
                currentAttributeName = attributeNames.nextElement();
                out.write("@attribute '"+currentAttributeName+"' {"+type+"}\n");                  
                attributeValuesMatrix[counter++] = dataset.get(currentAttributeName);
            }
            out.write("@data\n");
 
            String currentInstance = "";
            System.out.println("numberOfInstances = "+numberOfInstances);
            for(int i=0; i < numberOfInstances; i++){                  
                for (int j=0; j < numberOfAttributes; j++){        
                    currentInstance += attributeValuesMatrix[j][i]+",";
                }
                currentInstance = currentInstance.substring(0, currentInstance.lastIndexOf(','))+"\n";
                //System.out.println(currentInstance);
                out.write(currentInstance);  
                currentInstance ="";
            }
            out.close();
            InputStream istrm = new FileInputStream (instancesFile);
            loader.setSource(istrm);
            
        }catch (Exception e){
            System.err.println("Error: " + e.getMessage());
        };

    }
    
    public void findClusters(String clustererName, String[] options){
        
        createARFFFile();
        
        //instantiate the cluster;       
        
        try{

//            curClusterer = AbstractClusterer.forName(clustererName, options);
            Class m_class = Class.forName("weka.clusterers."+clustererName);
            curClusterer = (Clusterer)m_class.newInstance();
            curClusterer.buildClusterer(loader.getDataSet());
            numberOfClusters = curClusterer.numberOfClusters();

        }catch(Exception e){        
            System.err.println("Error: " + e.getStackTrace());
        }
        
    };

    
    private int getNumberOfClusters(){
        return numberOfClusters;
    }
    
    @Override
    public String toString(){
        return curClusterer.toString();        
    }
    
    public double[] getClusteredInstances(){
        
        String toBeParsed = toString();
        int startIndex = toBeParsed.indexOf("Cluster#");
        int endIndex = toBeParsed.indexOf(")\n======")+1;
        toBeParsed = toBeParsed.substring(startIndex, endIndex);
        toBeParsed = toBeParsed.substring(toBeParsed.indexOf("("));
        toBeParsed = toBeParsed.replaceAll("\\(", "").replaceAll("\\)", "");
        
        StringTokenizer st = new StringTokenizer(toBeParsed);
        int counter = 0;
        double[] m_clusteredInstances = new double[st.countTokens()-1];
        st.nextToken();
        while (st.hasMoreTokens()) {
            m_clusteredInstances[counter++] = (100.0*Integer.parseInt(st.nextToken())/numberOfInstances);

        }
        return m_clusteredInstances;
    }
    
    public Instances getInstances(){
        return m_instances;
    }
    
    public static void demo(String args[]){
        //test code
        //example of clusterer name
        String clustererName = "SimpleKMeans";
        
        //example of instances
        String[] gpi_kpi_1 ={"red", "yellow","red", "green" , "red","red", "green","green","red","red"};
        String[] gpi_kpi_2 ={"red", "green","red", "yellow", "red", "green", "yellow", "green" , "red","red"};
        String[] gpi_kpi_3 ={"green", "green","green", "green","yellow", "green" , "red", "green", "red", "yellow"};
        String[] gpi_kpi_4 ={"yellow", "green", "green", "red", "green" , "red","red", "red","green","red"};
        
        //instantiate the ClusterMiner
        ClusterMiner testClusterer = new ClusterMiner();
        
        //load the instances
        testClusterer.load("gpi_kpi_1", gpi_kpi_1);
        testClusterer.load("gpi_kpi_2", gpi_kpi_2);
        testClusterer.load("gpi_kpi_3", gpi_kpi_3);
        testClusterer.load("gpi_kpi_4", gpi_kpi_4);

        //define options  
        

         /**       
         <!-- options-start -->
         * Valid options are: <p/>
         * 
         * <pre> -N &lt;num&gt;
         *  number of clusters.
         *  (default 2).</pre>
         * 
         * <pre> -V
         *  Display std. deviations for centroids.
         * </pre>
         * 
         * <pre> -M
         *  Replace missing values with mean/mode.
         * </pre>
         * 
         * <pre> -S &lt;num&gt;
         *  Random number seed.
         *  (default 10)</pre>
         * 
         * <pre> -A &lt;classname and options&gt;
         *  Distance function to be used for instance comparison
         *  (default weka.core.EuclidianDistance)</pre>
         * 
         * <pre> -I &lt;num&gt;
         *  Maximum number of iterations. </pre>
         * 
         * <pre> -O 
         *  Preserve order of instances. </pre>

         **/   
        
        String[] options = {
            "-N","2",
            "-A","weka.core.EuclideanDistance",
            "-R","first-last",
            "-I","500",
            "-S", "10"
        };
        
        //set options and find clusters
        testClusterer.findClusters(clustererName,options);
   
        System.out.println("Clusterer Output");
        System.out.println("Number of Clusters: "+testClusterer.getNumberOfClusters());
        
        double[] clusteredInstances = testClusterer.getClusteredInstances();
        for(int k = 0; k < clusteredInstances.length; k++){
            System.out.println("["+k+"]: "+clusteredInstances[k]+"%");
        }
    }
}
    

